﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BawangMemo.BackendModels
{
    public class CourseWrapper
    {
        public CourseInfo CourseInfo { get; set; }

        private List<WordWrapper> _wordList;

        private List<WordCard> _wordCardList;

        private List<string> _wordSpellList;

        private WordWrapper nextRecite;

        private IReciter reciter;

        public CourseWrapper(CourseInfo courseInfo)
        {
            CourseInfo = courseInfo;
            //_wordCardList = WordRepository.LoadWordCardsFromCourse(this);
            //_wordSpellList = (from elem in WordCardList
//                              select elem.Spell.ToLower()).ToList();
        }

        public CourseWrapper(string courseName, int bucketSize, int totalWordCount)
            : this(new CourseInfo(courseName, bucketSize, totalWordCount))
        {
        }

        public WordCard GetWordCard(int wordId)
        {
            return WordCardList[wordId - 1];
        }

        #region Info Methods

        public double Progress
        {
            get
            {
                return OldWordsCount * 1.0 / WordList.Count;
            }
        }

        #endregion

        #region Recite Methods

        public void StartRecite()
        {
            var infos = (from ww in WordList select ww.WordInfo).ToList();
            reciter = new Algo(infos, CurrentWordIndex, BucketSize);

            WordRepository.SaveAllWordInfo(infos, this.WordsInfoPath);
            nextRecite = GetWrapper(reciter.NextWord());
            CourseInfo.CurrentWordIndex = reciter.CurrentWordIndex;
            CourseInfo.CurrentBucketId = reciter.CurrentBucketId;
            CourseRepository.SaveCourseInfo(CourseInfo, CourseInfoPath);
        }

        public bool HasNextWordToRecite()
        {
            return nextRecite != null;
        }

        public WordWrapper NextRecite
        {
            get
            {
                return nextRecite;
            }
        }

        public void UserResponse(WordWrapper wi, int proficiency)
        {
            // log the record
            RecordRepository.LogMemRecord(wi, proficiency);

            // propagates to the reciting algorithm
            reciter.UserResponse(wi.WordInfo, proficiency);

            // persists the WordInfo state
            WordRepository.SaveWordInfo(WordsInfoPath, wi.WordInfo);

            // cache the next word
            nextRecite = GetWrapper(reciter.NextWord());
            CourseInfo.CurrentWordIndex = reciter.CurrentWordIndex;
            CourseInfo.CurrentBucketId = reciter.CurrentBucketId;
            CourseRepository.SaveCourseInfo(CourseInfo, CourseInfoPath);
        }

        public int TodayTotal
        {
            get { return reciter.TodayTotal; }
        }

        public int TodayRecited
        {
            get { return reciter.TodayRecited; }
        }

        public int OldWordsCount
        {
            get
            {
                //return WordList.Count(wordWrapper => !wordWrapper.IsNewWord());
                return this.CurrentWordIndex +
                    ((this.CurrentWordIndex < this.TotalWordCount && !WordList[this.CurrentWordIndex].IsNewWord()) ? 1 : 0);
            }
        }

        #endregion

        #region TestView Methods

        public List<WordWrapper> ListOldWords()
        {
            return WordList.Where(wordWrapper => !wordWrapper.IsNewWord()).ToList();
        }

        #endregion

        #region Bank Methods

        public List<WordWrapper> ListPrefixWords(string prefix)
        {
            var result = new List<WordWrapper>();
            for (int i = 0; i < WordList.Count; i++)
            {
                if (WordSpellList[i].StartsWith(prefix))
                {
                    result.Add(WordList[i]);
                }
            }
            return result;
        }

        #endregion

        #region Private Helpers

        private WordWrapper GetWrapper(WordInfo wi)
        {
            return wi != null ? WordList[wi.WordId - 1] : null;
        }

        #endregion 

        #region Properties
        public List<WordWrapper> WordList
        {
            get
            {
                if (_wordList == null)
                {
                    _wordList = WordRepository.LoadWordsFromCourse(this);
                }
                return _wordList;
            }
        }

        public List<WordCard> WordCardList
        {
            get
            {
                if (_wordCardList == null)
                {
                    _wordCardList = WordRepository.LoadWordCardsFromCourse(this);
                }
                return _wordCardList;
            }
        }

        public List<string> WordSpellList
        {
            get
            {
                if (_wordSpellList == null)
                {
                    _wordSpellList = (from elem in WordCardList
                                      select elem.Spell.ToLower()).ToList();
                }
                return _wordSpellList;
            }
        }

        private IDictionary<string, List<WordWrapper>> _wordDict;

        public IDictionary<string, List<WordWrapper>> WordDict
        {
            get
            {
                if (_wordDict == null)
                {
                    _wordDict = new Dictionary<string, List<WordWrapper>>();
                    foreach (WordWrapper ww in WordList)
                    {
                        //string spell = ww.Spell.ToLower();
                        if (_wordDict.ContainsKey(ww.Spell))
                        {
                            _wordDict[ww.Spell].Add(ww);
                        }
                        else
                        {
                            _wordDict.Add(ww.Spell, new List<WordWrapper>() { ww });
                        }
                    }
                }
                return _wordDict;
            }
        }

        public string CourseName
        {
            get { return CourseInfo.CourseName; }
        }

        public int BucketSize
        {
            get { return CourseInfo.BucketSize; }
        }

        public int CurrentWordIndex
        {
            get { return CourseInfo.CurrentWordIndex; }
        }

        public int CurrentBucket
        {
            get { return CourseInfo.CurrentBucketId; }
        }

        /// <summary>
        /// The path of the word list that makes a copy of the book
        /// but words in which may be in different orders.
        /// </summary>
        public string WordListPath
        {
            get
            {
                return Constants.CourseDirectory + CourseInfo.CourseName + Constants.EXT_WORD_LIST;
            }
        }

        /// <summary>
        /// The info (data for the recite algorithm) of the words,
        /// of which the order is adhere to the word list of the course.
        /// </summary>
        public string WordInfoPath
        {
            get
            {
                return Constants.CourseDirectory + CourseInfo.CourseName + Constants.EXT_WORD_INFO;
            }
        }

        /// <summary>
        /// The path of the tag file (database)
        /// </summary>
        public string WordTagPath
        {
            get
            {
                return Constants.CourseDirectory + CourseInfo.CourseName + Constants.EXT_WORD_TAG;
            }
        }

        /// <summary>
        /// The path of the file that contains the metadata
        /// of the course, such as bucket size, current index of memo.
        /// </summary>
        public string CourseInfoPath
        {
            get
            {
                return Constants.CourseDirectory + CourseInfo.CourseName + Constants.EXT_COURSE_INFO;
            }
        }

        /// <summary>
        /// The path of the file that contains the metadata
        /// of the words of the course, WordInfo.
        /// </summary>
        public string WordsInfoPath
        {
            get
            {
                return Constants.CourseDirectory + CourseInfo.CourseName + Constants.EXT_WORD_INFO;
            }
        }
        #endregion

        #region Statistics

        public int TotalWordCount
        {
            get
            {
                if (CourseInfo.TotalWordCount <= 0)
                {
                    CourseInfo.TotalWordCount = WordList.Count;
                    CourseRepository.SaveCourse(this);
                }
                return CourseInfo.TotalWordCount;
            }
        }

        public int TotalBucketCount
        {
            get
            {
                if (reciter == null)
                {
                    var infos = (from ww in WordList select ww.WordInfo).ToList();
                    reciter = new Algo(infos, CurrentWordIndex, BucketSize);                    
                }
                return reciter.TotalBucket;
            }
        }

        #endregion

        public int MaxTestSize
        {
            get
            {
                try
                {
                    ListDataModel data = ListRepository.GetListData(this, 1);
                    return Int32.Parse(data.ListCount) > 100 ? Int32.Parse(data.ListCount) : 100;
                }
                catch
                {
                    return 100;
                }
            }
        }
    }
}
